/**
 * 通用工具函数
 */

/**
 * 类型检查函数
 */
export const typeChecks = {
  isString(val: unknown): val is string {
    return typeof val === 'string'
  },
  
  isNumber(val: unknown): val is number {
    return typeof val === 'number' && !isNaN(val)
  },
  
  isBoolean(val: unknown): val is boolean {
    return typeof val === 'boolean'
  },
  
  isFunction(val: unknown): val is Function {
    return typeof val === 'function'
  },
  
  isObject(val: unknown): val is Record<any, any> {
    return val !== null && typeof val === 'object' && !Array.isArray(val)
  },
  
  isArray(val: unknown): val is any[] {
    return Array.isArray(val)
  },
  
  isDate(val: unknown): val is Date {
    return val instanceof Date
  },
  
  isNull(val: unknown): val is null {
    return val === null
  },
  
  isUndefined(val: unknown): val is undefined {
    return val === undefined
  },
  
  isNullOrUndefined(val: unknown): val is null | undefined {
    return val === null || val === undefined
  },
  
  isEmpty(val: unknown): boolean {
    if (val === null || val === undefined) return true
    if (typeof val === 'string' || Array.isArray(val)) return val.length === 0
    if (typeof val === 'object') return Object.keys(val).length === 0
    return false
  }
}

/**
 * 深拷贝函数
 * @param obj 要拷贝的对象
 * @returns 深拷贝后的对象
 */
export function deepClone<T>(obj: T): T {
  if (obj === null || typeof obj !== 'object') {
    return obj
  }
  
  // 处理日期对象
  if (obj instanceof Date) {
    return new Date(obj.getTime()) as any
  }
  
  // 处理数组
  if (Array.isArray(obj)) {
    return obj.map(item => deepClone(item)) as any
  }
  
  // 处理普通对象
  const cloned: any = {}
  for (const key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      cloned[key] = deepClone((obj as any)[key])
    }
  }
  
  return cloned
}

/**
 * 对象合并
 * @param target 目标对象
 * @param sources 源对象列表
 * @returns 合并后的对象
 */
export function merge<T extends Record<string, any>>(
  target: T,
  ...sources: Record<string, any>[]
): T {
  if (!sources.length) return target
  
  const source = sources.shift()
  if (source === undefined) return target
  
  if (typeChecks.isObject(target) && typeChecks.isObject(source)) {
    for (const key in source) {
      if (typeChecks.isObject(source[key])) {
        if (!target[key]) Object.assign(target, { [key]: {} })
        merge(target[key], source[key])
      } else {
        Object.assign(target, { [key]: source[key] })
      }
    }
  }
  
  return merge(target, ...sources)
}

/**
 * 生成唯一ID
 * @param prefix ID前缀
 * @returns 唯一ID
 */
export function generateId(prefix = ''): string {
  const timestamp = Date.now()
  const random = Math.floor(Math.random() * 10000)
  return `${prefix}${timestamp}${random}`
}

/**
 * 睡眠函数
 * @param ms 毫秒数
 * @returns Promise
 */
export function sleep(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms))
}

/**
 * 安全地获取嵌套对象属性
 * @param obj 对象
 * @param path 属性路径，如 'user.address.city'
 * @param defaultValue 默认值
 * @returns 属性值或默认值
 */
export function get(
  obj: Record<string, any> | null | undefined,
  path: string,
  defaultValue?: any
): any {
  if (obj == null) return defaultValue
  
  const keys = path.split('.')
  let result = obj
  
  for (const key of keys) {
    result = result?.[key]
    if (result === undefined) return defaultValue
  }
  
  return result
}

/**
 * 安全地设置嵌套对象属性
 * @param obj 对象
 * @param path 属性路径，如 'user.address.city'
 * @param value 要设置的值
 */
export function set(
  obj: Record<string, any>,
  path: string,
  value: any
): void {
  if (!obj || typeof obj !== 'object') return
  
  const keys = path.split('.')
  const lastKey = keys.pop()
  
  if (!lastKey) return
  
  let current = obj
  
  for (const key of keys) {
    if (current[key] === undefined) {
      current[key] = {}
    }
    current = current[key]
  }
  
  current[lastKey] = value
}

/**
 * 对象转查询字符串
 * @param obj 对象
 * @returns 查询字符串
 */
export function objectToQueryString(obj: Record<string, any>): string {
  return Object.keys(obj)
    .filter(key => obj[key] != null)
    .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`)
    .join('&')
}

/**
 * 查询字符串转对象
 * @param queryString 查询字符串
 * @returns 对象
 */
export function queryStringToObject(queryString: string): Record<string, string> {
  if (!queryString || !queryString.trim()) return {}
  
  const query = queryString.startsWith('?') 
    ? queryString.substring(1) 
    : queryString
  
  return query
    .split('&')
    .reduce((acc: Record<string, string>, item) => {
      const [key, value] = item.split('=')
      if (key) {
        acc[decodeURIComponent(key)] = decodeURIComponent(value || '')
      }
      return acc
    }, {})
}

/**
 * 首字母大写
 * @param str 字符串
 * @returns 首字母大写的字符串
 */
export function capitalize(str: string): string {
  if (!str) return ''
  return str.charAt(0).toUpperCase() + str.slice(1)
}

/**
 * 驼峰转连字符
 * @param str 驼峰字符串
 * @returns 连字符字符串
 */
export function camelToKebab(str: string): string {
  return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()
}

/**
 * 连字符转驼峰
 * @param str 连字符字符串
 * @returns 驼峰字符串
 */
export function kebabToCamel(str: string): string {
  return str.replace(/-([a-z])/g, (_, c) => c.toUpperCase())
}

/**
 * 查看数组中是否存在目标元素
 * @param array 要搜索的数组
 * @param target 目标元素
 * @returns 是否存在
 */
export function contains<T>(array: T[], target: T): boolean {
  for (const item of array) {
    if (item === target) {
      return true
    }
  }
  return false
}

/**
 * 查看字符串中是否包含任意子字符串
 * @param targetStr 目标字符串
 * @param str 要搜索的子字符串列表
 * @returns 是否包含任意子字符串
 */
export function containsAny(targetStr: string, ...str: string[]): boolean {
  for (const cur of str) {
    if (targetStr.search(cur) !== -1) {
      return true
    }
  }
  return false
}

/**
 * 将对象中的空字符串转换为null
 * @param obj 要处理的对象
 * @param ignore 要忽略的字段列表
 */
export function trimFieldsEmptyToNull<T extends Record<string, any | null>>(
  obj: T,
  ignore: string[] = []
): void {
  for (const key in obj) {
    if (contains(ignore, key)) {
      continue
    }
    if (obj[key] === '') {
      (obj as Record<string, any>)[key] = null
    }
  }
}

/**
 * 将对象中的字段转换为空字符串
 * @param obj 要处理的对象
 * @param ignore 要忽略的字段列表
 */
export function trimFieldsToEmpty<T extends Record<string, any>>(
  obj: T,
  ignore: string[] = []
): void {
  for (const key in obj) {
    if (contains(ignore, key)) {
      continue
    }
    if (obj[key] === '') {
      (obj as Record<string, any>)[key] = null
    }
  }
}

/**
 * 获取当天0点时间戳
 * @returns 时间戳
 */
export function getBeginOfDayAsTimestamp(): number {
  const now = new Date()
  now.setHours(0)
  now.setMinutes(0)
  now.setSeconds(0)
  now.setMilliseconds(0)
  return now.getTime()
}

/**
 * 获取当前日期格式化字符串
 * @returns 格式化的日期字符串 (YYYY-MM-DD)
 */
export function getCurrentDateFormat(): string {
  const date = new Date()
  const year = date.getFullYear()
  const month = String(date.getMonth() + 1).padStart(2, '0')
  const day = String(date.getDate()).padStart(2, '0')
  return `${year}-${month}-${day}`
}

/**
 * 获取上月日期格式化字符串
 * @returns 格式化的日期字符串 (YYYY-MM-DD)
 */
export function getLastMonthDateFormat(): string {
  const date = new Date()
  let year = date.getFullYear()
  let month = date.getMonth() + 1
  
  if (month === 1) {
    month = 12
    year--
  } else {
    month--
  }
  
  const monthStr = String(month).padStart(2, '0')
  const dayStr = String(date.getDate()).padStart(2, '0')
  return `${year}-${monthStr}-${dayStr}`
}

/**
 * 浅拷贝对象
 * @param obj 要拷贝的对象
 * @returns 拷贝后的新对象
 */
export function copyShallow<T extends Record<string, any>>(obj: T): T {
  const copyObj: Partial<T> = {}
  for (const key in obj) {
    copyObj[key] = obj[key]
  }
  return copyObj as T
}

/**
 * 只拷贝已被修改的变量
 * @param oldObj 旧对象
 * @param obj 新对象
 * @param copyFields 要拷贝的字段列表
 * @param ignore 忽略变更判断的字段列表
 * @returns 包含修改字段的新对象，如果没有修改则返回null
 */
export function copyShallowOnlyModified<T extends Record<string, any>>(
  oldObj: T,
  obj: T,
  copyFields: string[] = [],
  ignore: string[] = []
): Partial<T> | null {
  const copyObj: Partial<T> = {}
  let hasModified = false
  
  for (const key in obj) {
    if (contains(copyFields, key)) {
      if (contains(ignore, key)) {
        copyObj[key] = obj[key]
        continue
      }
      if (oldObj[key] !== obj[key]) {
        copyObj[key] = obj[key]
        hasModified = true
      }
    }
  }
  
  return hasModified ? copyObj : null
}

/**
 * 拷贝数组
 * @param target 要拷贝的数组
 * @returns 拷贝后的新数组
 */
export function copyArray<T>(target: T[]): T[] {
  return [...target]
}

/**
 * 数字递增动画
 * @param target 目标对象
 * @param num 目标数字
 */
export function numberSurge(
  target: { num: number },
  num: number
): void {
  const intervalTime = num / 30.0
  const timer = setInterval(() => {
    target.num = target.num + 1
    if (num === target.num) {
      clearInterval(timer)
    }
  }, intervalTime)
}

/**
 * 时间戳转字符串
 * @param timestamp 时间戳
 * @returns 格式化的时间字符串 (YYYY-MM-DD HH:mm:ss)
 */
export function timestamp2Str(timestamp: number): string {
  const date = new Date(timestamp)
  const Y = date.getFullYear() + '-'
  const M = String(date.getMonth() + 1).padStart(2, '0') + '-'
  const D = fill0(date.getDate()) + ' '
  const h = fill0(date.getHours()) + ':'
  const m = fill0(date.getMinutes()) + ':'
  const s = fill0(date.getSeconds())
  return Y + M + D + h + m + s
}

/**
 * 个位数填充零
 * @param num 数字
 * @returns 填充零后的字符串
 */
export function fill0(num: number): string {
  return num < 10 ? '0' + num : String(num)
}